package org.br.plotter.functions.transformer;

import org.br.plotter.functions.*;
import org.br.plotter.parser.ParseException;

import java.util.*;

public abstract class ArrayTransformer {
 
	public List<double[]> getRangeArray(double min, double max, int points,
			String equation) throws ParseException {
		Function f = Function.parseFunction(equation);
		List<double[]> pointsList = new ArrayList<double[]>();
		double step = (max - min) / points;
		for (double d = min; d < max; d += step) {
			double[] point = eval(f, d);
			pointsList.add(point);
		}
		return pointsList;
	}

	public static List<double[]> getRangeArray(double min, double max,
			int points, String equationX, String equationY)
			throws ParseException {
		Function fx = Function.parseFunction(equationX);
		Function fy = Function.parseFunction(equationY);
		List<double[]> pointData = new ArrayList<double[]>();
		double step = (max - min) / points;
		for (double d = min; d < max; d += step) {
			double[] point = { fx.eval(d), fy.eval(d) };
			pointData.add(point);
		}
		return pointData;
	}

	private static double[] getMinMax(List<double[]> array) {
		double[] point = array.get(0);
		double minx = point[0];
		double maxx = point[0];
		double miny = point[1];
		double maxy = point[1];
		for (double[] d : array) {
			minx = Math.min(d[0], minx);
			maxx = Math.max(d[0], maxx);
			miny = Math.min(d[1], miny);
			maxy = Math.max(d[1], maxy);

		}
		double[] result = { minx, maxx, miny, maxy };
		return result;
	}

	public static double [] calculateDefaultScale(List<double[]> data, int width,
			int height) {
		double[] ranges = getMinMax(data);
		int x = (int) (width / (ranges[1] - ranges[0]));
		if(x < 10)
			x=10;
		int y = (int) (height / (ranges[3] - ranges[2]));
		if(y < 10)
			y = 10;
		double[] result = { x, y, width / 2, height / 2 };
		return result;
	}

	public static int[][] transformByScale(List<double[]> data, double[] scale) {
		int size = data.size();
		int scaleX = (int)scale[0];
		int scaleY = (int)scale[1];
		int centralPointX = (int)scale[2];
		int centralPointy =(int) scale[3];

		int[][] result = new int[2][size];
		int i = 0;
		for (double[] d : data) {
			result[0][i] = (int) (scaleX * d[0] + centralPointX);
			result[1][i++] = (int) (-scaleY * d[1] + centralPointy);
		}
		return result;
	}

	abstract double[] eval(Function function, double x);

	final public static ArrayTransformer DefaultTransformer = new ArrayTransformer() {
		double[] eval(Function function, double x) {
			double[] result = { x, function.eval(x) };
			return result;
		}
	};

	final public static ArrayTransformer PolarTransformer = new ArrayTransformer() {
		double[] eval(Function function, double t) {			
			double r = function.eval(t);
			double x = r * Math.cos(t);
			double y = r * Math.sin(t);
			double[] result = { x, y };
			return result;

		}
	};

}